home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / strings.c < prev    next >
C/C++ Source or Header  |  1996-07-09  |  49KB  |  2,085 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: strings.c,v 4.48 1996/07/09 22:23:27 mikes Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. /*======================================================================
  43.     strings.c
  44.     Misc extra and useful string functions
  45.       - rplstr         replace a substring with another string
  46.       - sqzspaces      Squeeze out the extra blanks in a string
  47.       - sqzsnewlines   Squeeze out \n and \r.
  48.       - remove_trailing_white_space 
  49.       - remove_leading_white_space 
  50.       - strclean       Remove leading and trailing white space and convert
  51.                        to lower case
  52.       - strucmp        A case insensitive strcmp
  53.       - struncmp       A case insensitve strncmp
  54.       - srchstr        Search a string for a sub string
  55.       - strindex       Replacement for strchr/index
  56.       - strrindex      Replacement for strrchr/rindex
  57.       - sstrcpy        Copy one string onto another, advancing dest'n pointer
  58.       - istrncpy       Copy n chars between bufs, making ctrl chars harmless
  59.       - month_abbrev   Return three letter abbreviations for months
  60.       - month_num      Calculate month number from month/year string
  61.       - cannon_date    Formalize format of a some what formatted date
  62.       - pretty_command Return nice string describing character
  63.       - blanks         Returns a string n blanks long
  64.       - comatose       Format number with nice commas
  65.       - byte_string    Format number of bytes with Kb, Mb, Gb or bytes
  66.       - enth-string    Format number i.e. 1: 1st, 983: 983rd....
  67.       - read_hex       Convert 1 or 2-digit hex string to integer
  68.       - string_to_cstring  Convert string to C-style constant string with \'s
  69.       - cstring_to_hexstring  Convert cstring to hex string
  70.  
  71.  ====*/
  72.  
  73. #include "headers.h"
  74.  
  75. void char_to_hex_pair PROTO((int, char *));
  76. void char_to_octal_triple PROTO((int, char *));
  77. int  read_octal PROTO((char *));
  78.  
  79.  
  80. /*----------------------------------------------------------------------
  81.        Replace n characters in one string with another given string
  82.  
  83.    args: os -- the output string
  84.          dl -- the number of character to delete from start of os
  85.          is -- The string to insert
  86.   
  87.  Result: returns pointer in originl string to end of string just inserted
  88.          First 
  89.   ---*/
  90. char *
  91. rplstr(os,dl,is)
  92. char *os,*is;
  93. int dl;
  94. {   
  95.     register char *x1,*x2,*x3;
  96.     int           diff;
  97.  
  98.     if(os == NULL)
  99.         return(NULL);
  100.        
  101.     for(x1 = os; *x1; x1++);
  102.     if(dl > x1 - os)
  103.         dl = x1 - os;
  104.         
  105.     x2 = is;      
  106.     if(is != NULL){
  107.         while(*x2++);
  108.         x2--;
  109.     }
  110.  
  111.     if((diff = (x2 - is) - dl) < 0){
  112.         x3 = os; /* String shrinks */
  113.         if(is != NULL)
  114.             for(x2 = is; *x2; *x3++ = *x2++); /* copy new string in */
  115.         for(x2 = x3 - diff; *x2; *x3++ = *x2++); /* shift for delete */
  116.         *x3 = *x2;
  117.     } else {                
  118.         /* String grows */
  119.         for(x3 = x1 + diff; x3 >= os + (x2 - is); *x3-- = *x1--); /* shift*/
  120.         for(x1 = os, x2 = is; *x2 ; *x1++ = *x2++);
  121.         while(*x3) x3++;                 
  122.     }
  123.     return(x3);
  124. }
  125.  
  126.  
  127.  
  128. /*----------------------------------------------------------------------
  129.      Squeeze out blanks 
  130.   ----------------------------------------------------------------------*/
  131. void
  132. sqzspaces(string)
  133.      char *string;
  134. {
  135.     char *p = string;
  136.  
  137.     while(*string = *p++)           /* while something to copy       */
  138.       if(!isspace((unsigned char)*string)) /* only really copy if non-blank */
  139.     string++;
  140. }
  141.  
  142.  
  143.  
  144. /*----------------------------------------------------------------------
  145.      Squeeze out CR's and LF's 
  146.   ----------------------------------------------------------------------*/
  147. void
  148. sqznewlines(string)
  149.     char *string;
  150. {
  151.     char *p = string;
  152.  
  153.     while(*string = *p++)              /* while something to copy  */
  154.       if(*string != '\r' && *string != '\n')  /* only copy if non-newline */
  155.     string++;
  156. }
  157.  
  158.  
  159.  
  160. /*----------------------------------------------------------------------  
  161.        Remove leading white space from a string in place
  162.   
  163.   Args: string -- string to remove space from
  164.   ----*/
  165. void
  166. removing_leading_white_space(string)
  167.      char *string;
  168. {
  169.     char *p;
  170.  
  171.     /* ignore the null/non-blank string */
  172.     if(*(p = string) && isspace((unsigned char)*p)){
  173.     /* find the first non-blank  */
  174.     while(*p && isspace((unsigned char)*p))
  175.       p++;
  176.  
  177.     while(*string++ = *p++)        /* copy from there... */
  178.       ;
  179.     }
  180. }
  181.  
  182.  
  183.  
  184. /*----------------------------------------------------------------------  
  185.        Remove trailing white space from a string in place
  186.   
  187.   Args: string -- string to remove space from
  188.   ----*/
  189. void
  190. removing_trailing_white_space(string)
  191.      char *string;
  192. {
  193.     char *p = NULL;
  194.  
  195.     for(; *string; string++)        /* remember start of whitespace */
  196.       p = (!isspace((unsigned char)*string)) ? NULL : (!p) ? string : p;
  197.  
  198.     if(p)                /* if whitespace, blast it */
  199.       *p = '\0';
  200. }
  201.  
  202.  
  203.  
  204. /*----------------------------------------------------------------------  
  205.        Remove quotes from a string in place
  206.   
  207.   Args: string -- string to remove quotes from
  208.   Rreturns: string passed us, but with quotes gone
  209.   ----*/
  210. char *
  211. removing_quotes(string)
  212.     char *string;
  213. {
  214.     register char *p, *q;
  215.  
  216.     if(*(p = q = string) == '\"'){
  217.     do
  218.       if(*q == '\"' || *q == '\\')
  219.         q++;
  220.     while(*p++ = *q++);
  221.     }
  222.  
  223.     return(string);
  224. }
  225.  
  226.  
  227.  
  228. /*--------------------------------------------------
  229.      A case insensitive strcmp()     
  230.   
  231.    Args: o, r -- The two strings to compare
  232.  
  233.  Result: integer indicating which is greater
  234.   ---*/
  235. strucmp(o, r)
  236.     register char *o, *r;
  237. {
  238.     if(o == NULL){
  239.     if(r == NULL)
  240.       return 0;
  241.     else
  242.       return -1;
  243.     }
  244.     else if(r == NULL)
  245.       return 1;
  246.  
  247.     while(*o && *r
  248.       && ((isupper((unsigned char)(*o))
  249.                   ? (unsigned char)tolower((unsigned char)(*o))
  250.                   : (unsigned char)(*o))
  251.          == (isupper((unsigned char)(*r))
  252.                   ? (unsigned char)tolower((unsigned char)(*r))
  253.                   : (unsigned char)(*r)))){
  254.     o++;
  255.     r++;
  256.     }
  257.  
  258.     return((isupper((unsigned char)(*o))
  259.                 ? tolower((unsigned char)(*o))
  260.                 : (int)(unsigned char)(*o))
  261.        - (isupper((unsigned char)(*r))
  262.                     ? tolower((unsigned char)(*r))
  263.                 : (int)(unsigned char)(*r)));
  264. }
  265.  
  266.  
  267. /*---------------------------------------------------
  268.      Remove leading whitespace, trailing whitespace and convert 
  269.      to lowercase
  270.  
  271.    Args: s, -- The string to clean
  272.  
  273.  Result: the cleaned string
  274.   ----*/
  275. char *
  276. strclean(string)
  277.      char *string;
  278. {
  279.     char *s = string, *sc = NULL, *p = NULL;
  280.  
  281.     for(; *s; s++){                /* single pass */
  282.     if(!isspace((unsigned char)*s)){
  283.         p = NULL;                /* not start of blanks   */
  284.         if(!sc)                /* first non-blank? */
  285.           sc = string;            /* start copying */
  286.     }
  287.     else if(!p)                /* it's OK if sc == NULL */
  288.       p = sc;                /* start of blanks? */
  289.  
  290.     if(sc)                    /* if copying, copy */
  291.       *sc++ = isupper((unsigned char)(*s))
  292.               ? (unsigned char)tolower((unsigned char)(*s))
  293.               : (unsigned char)(*s);
  294.     }
  295.  
  296.     if(p)                    /* if ending blanks  */
  297.       *p = '\0';                /* tie off beginning */
  298.     else if(!sc)                /* never saw a non-blank */
  299.       *string = '\0';                /* so tie whole thing off */
  300.  
  301.     return(string);
  302. }
  303.  
  304.  
  305.  
  306. /*----------------------------------------------------------------------
  307.      A case insensitive strncmp()     
  308.   
  309.    Args: o, r -- The two strings to compare
  310.          n    -- length to stop comparing strings at
  311.  
  312.  Result: integer indicating which is greater
  313.    
  314.   ----*/
  315. struncmp(o, r, n)
  316.     register char *o,
  317.           *r;
  318.     register int   n;
  319. {
  320.     if(o == NULL){
  321.     if(r == NULL)
  322.       return 0;
  323.     else
  324.       return -1;
  325.     }
  326.     else if(r == NULL)
  327.       return 1;
  328.  
  329.     n--;
  330.     while(n && *o && *r
  331.       && ((isupper((unsigned char)(*o))
  332.                   ? (unsigned char)tolower((unsigned char)(*o))
  333.                   : (unsigned char)(*o))
  334.          == (isupper((unsigned char)(*r))
  335.                   ? (unsigned char)tolower((unsigned char)(*r))
  336.                   : (unsigned char)(*r)))){
  337.     o++;
  338.     r++;
  339.     n--;
  340.     }
  341.  
  342.     return((isupper((unsigned char)(*o))
  343.                 ? tolower((unsigned char)(*o))
  344.                 : (int)(unsigned char)(*o))
  345.        - (isupper((unsigned char)(*r))
  346.                     ? tolower((unsigned char)(*r))
  347.                 : (int)(unsigned char)(*r)));
  348. }
  349.  
  350.  
  351.  
  352. /*----------------------------------------------------------------------
  353.         Search one string for another
  354.  
  355.    Args:  is -- The string to search in, the larger string
  356.           ss -- The string to search for, the smaller string
  357.  
  358.    Search for any occurance of ss in the is, and return a pointer
  359.    into the string is when it is found. The search is case indepedent.
  360.   ----*/
  361.  
  362. char *        
  363. srchstr(is, ss)
  364. register char *is, *ss;
  365. {                    
  366.     register char *sx, *sy;
  367.     char          *ss_store, *rv;
  368.     char          temp[251];
  369.     
  370.     if(is == NULL || ss == NULL)
  371.       return(NULL);
  372.  
  373.     if(strlen(ss) > sizeof(temp) - 2)
  374.       ss_store = (char *)fs_get(strlen(ss) + 1);
  375.     else
  376.       ss_store = temp;
  377.  
  378.     for(sx = ss, sy = ss_store; *sx != '\0' ; sx++, sy++)
  379.       *sy = isupper((unsigned char)(*sx))
  380.               ? (unsigned char)tolower((unsigned char)(*sx))
  381.               : (unsigned char)(*sx);
  382.     *sy = *sx;
  383.  
  384.     rv = NULL;
  385.     while(*is != '\0'){
  386.         for(sx = is, sy = ss_store;
  387.         ((*sx == *sy)
  388.           || ((isupper((unsigned char)(*sx))
  389.              ? (unsigned char)tolower((unsigned char)(*sx))
  390.              : (unsigned char)(*sx)) == (unsigned char)(*sy))) && *sy;
  391.         sx++, sy++)
  392.        ;
  393.  
  394.         if(!*sy){
  395.             rv = is;
  396.             break;
  397.         }
  398.  
  399.         is++;
  400.     }
  401.  
  402.     if(ss_store != temp)
  403.       fs_give((void **)&ss_store);
  404.  
  405.     return(rv);
  406. }
  407.  
  408.  
  409.  
  410. /*----------------------------------------------------------------------
  411.     A replacement for strchr or index ...
  412.  
  413.     Returns a pointer to the first occurance of the character
  414.     'ch' in the specified string or NULL if it doesn't occur
  415.  
  416.  ....so we don't have to worry if it's there or not. We bring our own.
  417. If we really care about efficiency and think the local one is more
  418. efficient the local one can be used, but most of the things that take
  419. a long time are in the c-client and not in pine.
  420.  ----*/
  421. char *
  422. strindex(buffer, ch)
  423.     char *buffer;
  424.     int ch;
  425. {
  426.     do
  427.       if(*buffer == ch)
  428.     return(buffer);
  429.     while (*buffer++ != '\0');
  430.  
  431.     return(NULL);
  432. }
  433.  
  434.  
  435. /* Returns a pointer to the last occurance of the character
  436.  * 'ch' in the specified string or NULL if it doesn't occur
  437.  */
  438. char *
  439. strrindex(buffer, ch)
  440.     char *buffer;
  441.     int   ch;
  442. {
  443.     char *address = NULL;
  444.  
  445.     do
  446.       if(*buffer == ch)
  447.     address = buffer;
  448.     while (*buffer++ != '\0');
  449.     return(address);
  450. }
  451.  
  452.  
  453.  
  454. /*----------------------------------------------------------------------
  455.   copy the source string onto the destination string returning with
  456.   the destination string pointer at the end of the destination text
  457.  
  458.   motivation for this is to avoid twice passing over a string that's
  459.   being appended to twice (i.e., strcpy(t, x); t += strlen(t))
  460.  ----*/
  461. void
  462. sstrcpy(d, s)
  463.     char **d;
  464.     char *s;
  465. {
  466.     while((**d = *s++) != '\0')
  467.       (*d)++;
  468. }
  469.  
  470.  
  471. /*----------------------------------------------------------------------
  472.   copy at most n chars of the source string onto the destination string
  473.   returning pointer to start of destination and converting any undisplayable
  474.   characters to harmless character equivalents.
  475.  ----*/
  476. char *
  477. istrncpy(d, s, n)
  478.     char *d, *s;
  479.     int n;
  480. {
  481.     char *rv = d;
  482.  
  483.     do
  484.       if(F_OFF(F_PASS_CONTROL_CHARS, ps_global) && *s && CAN_DISPLAY(*s))
  485.     if(n-- >= 0){
  486.         *d++ = '^';
  487.  
  488.         if(n-- >= 0)
  489.           *d++ = *s++ + '@';
  490.     }
  491.     while(n-- >= 0 && (*d++ = *s++));
  492.  
  493.     return(rv);
  494. }
  495.  
  496.  
  497.  
  498. char *xdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL};
  499.  
  500. char *
  501. month_abbrev(month_num)
  502.      int month_num;
  503. {
  504.     static char *xmonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  505.         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
  506.     if(month_num < 1 || month_num > 12)
  507.       return("xxx");
  508.     return(xmonths[month_num - 1]);
  509. }
  510.  
  511. char *
  512. week_abbrev(week_day)
  513.      int week_day;
  514. {
  515.     return(xdays[week_day]);
  516. }
  517.  
  518.  
  519. days_in_month(month, year)
  520.      int month, year;
  521. {
  522.     static int d_i_m[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
  523.  
  524.     if(month == 2) 
  525.       return(((year%4) == 0 && (year%100) != 0) ? 29 : 28);
  526.     else
  527.       return(d_i_m[month]);
  528. }
  529.     
  530.  
  531.  
  532. /*----------------------------------------------------------------------
  533.       Return month number of month named in string
  534.   
  535.    Args: s -- string with 3 letter month abbreviation of form mmm-yyyy
  536.  
  537.  Result: Returns month number with January, year 1900, 2000... being 0;
  538.          -1 if no month/year is matched
  539.  ----*/
  540. int
  541. month_num(s)
  542.      char *s;
  543. {
  544.     int month, year;
  545.     int i;
  546.  
  547.     for(i = 0; i < 12; i++){
  548.         if(struncmp(month_abbrev(i+1), s, 3) == 0)
  549.           break;
  550.     }
  551.     if(i == 12)
  552.       return(-1);
  553.  
  554.     year = atoi(s + 4);
  555.     if(year == 0)
  556.       return(-1);
  557.  
  558.     month = (year < 100 ? year + 1900 : year)  * 12 + i;
  559.     return(month);
  560. }
  561.  
  562.  
  563. /*
  564.  * Structure containing all knowledge of symbolic time zones.
  565.  * To add support for a given time zone, add it here, but make sure
  566.  * the zone name is in upper case.
  567.  */
  568. static struct {
  569.     char  *zone;
  570.     short  len,
  571.            hour_offset,
  572.        min_offset;
  573. } known_zones[] = {
  574.     {"PST", 3, -8, 0},            /* Pacific Standard */
  575.     {"PDT", 3, -7, 0},            /* Pacific Daylight */
  576.     {"MST", 3, -7, 0},            /* Mountain Standard */
  577.     {"MDT", 3, -6, 0},            /* Mountain Daylight */
  578.     {"CST", 3, -6, 0},            /* Central Standard */
  579.     {"CDT", 3, -5, 0},            /* Central Daylight */
  580.     {"EST", 3, -5, 0},            /* Eastern Standard */
  581.     {"EDT", 3, -4, 0},            /* Eastern Daylight */
  582.     {"JST", 3,  9, 0},            /* Japan Standard */
  583.     {"GMT", 3,  0, 0},            /* Universal Time */
  584.     {"UT",  2,  0, 0},            /* Universal Time */
  585. #ifdef    IST_MEANS_ISREAL
  586.     {"IST", 3,  2, 0},            /* Israel Standard */
  587. #else
  588. #ifdef    IST_MEANS_INDIA
  589.     {"IST", 3,  5, 30},            /* India Standard */
  590. #endif
  591. #endif
  592.     {NULL, 0, 0},
  593. };
  594.  
  595. /*----------------------------------------------------------------------
  596.   Parse date in or near RFC-822 format into the date structure
  597.  
  598. Args: given_date -- The input string to parse
  599.       d          -- Pointer to a struct date to place the result in
  600.  
  601. Returns nothing
  602.  
  603. The following date fomrats are accepted:
  604.   WKDAY DD MM YY HH:MM:SS ZZ
  605.   DD MM YY HH:MM:SS ZZ
  606.   WKDAY DD MM HH:MM:SS YY ZZ
  607.   DD MM HH:MM:SS YY ZZ
  608.   DD MM WKDAY HH:MM:SS YY ZZ
  609.   DD MM WKDAY YY MM HH:MM:SS ZZ
  610.  
  611. All leading, intervening and trailing spaces tabs and commas are ignored.
  612. The prefered formats are the first or second ones.  If a field is unparsable
  613. it's value is left as -1. 
  614.  
  615.   ----*/
  616. void
  617. parse_date(given_date, d)
  618.      char        *given_date;
  619.      struct date *d;
  620. {
  621.     char *p, **i, *q, n;
  622.     int   month;
  623.  
  624.     d->sec   = -1;
  625.     d->minute= -1;
  626.     d->hour  = -1;
  627.     d->day   = -1;
  628.     d->month = -1;
  629.     d->year  = -1;
  630.     d->wkday = -1;
  631.     d->hours_off_gmt = -1;
  632.     d->min_off_gmt   = -1;
  633.  
  634.     if(given_date == NULL)
  635.       return;
  636.  
  637.     p = given_date;
  638.     while(*p && isspace((unsigned char)*p))
  639.       p++;
  640.  
  641.     /* Start with month, weekday or day ? */
  642.     for(i = xdays; *i != NULL; i++) 
  643.       if(struncmp(p, *i, 3) == 0) /* Match first 3 letters */
  644.         break;
  645.     if(*i != NULL) {
  646.         /* Started with week day */
  647.         d->wkday = i - xdays;
  648.         while(*p && !isspace((unsigned char)*p) && *p != ',')
  649.           p++;
  650.         while(*p && (isspace((unsigned char)*p) || *p == ','))
  651.           p++;
  652.     }
  653.     if(isdigit((unsigned char)*p)) {
  654.         d->day = atoi(p);
  655.         while(*p && isdigit((unsigned char)*p))
  656.           p++;
  657.         while(*p && (*p == '-' || *p == ',' || isspace((unsigned char)*p)))
  658.           p++;
  659.     }
  660.     for(month = 1; month <= 12; month++)
  661.       if(struncmp(p, month_abbrev(month), 3) == 0)
  662.         break;
  663.     if(month < 13) {
  664.         d->month = month;
  665.  
  666.     } 
  667.     /* Move over month, (or whatever is there) */
  668.     while(*p && !isspace((unsigned char)*p) && *p != ',' && *p != '-')
  669.        p++;
  670.     while(*p && (isspace((unsigned char)*p) || *p == ',' || *p == '-'))
  671.        p++;
  672.  
  673.     /* Check again for day */
  674.     if(isdigit((unsigned char)*p) && d->day == -1) {
  675.         d->day = atoi(p);
  676.         while(*p && isdigit((unsigned char)*p))
  677.           p++;
  678.         while(*p && (*p == '-' || *p == ',' || isspace((unsigned char)*p)))
  679.           p++;
  680.     }
  681.  
  682.     /*-- Check for time --*/
  683.     for(q = p; *q && isdigit((unsigned char)*q); q++);
  684.     if(*q == ':') {
  685.         /* It's the time (out of place) */
  686.         d->hour = atoi(p);
  687.         while(*p && *p != ':' && !isspace((unsigned char)*p))
  688.           p++;
  689.         if(*p == ':') {
  690.             p++;
  691.             d->minute = atoi(p);
  692.             while(*p && *p != ':' && !isspace((unsigned char)*p))
  693.               p++;
  694.             if(*p == ':') {
  695.                 d->sec = atoi(p);
  696.                 while(*p && !isspace((unsigned char)*p))
  697.                   p++;
  698.             }
  699.         }
  700.         while(*p && isspace((unsigned char)*p))
  701.           p++;
  702.     }
  703.     
  704.  
  705.     /* Get the year 0-49 is 2000-2049; 50-100 is 1950-1999 and
  706.                                            101-9999 is 101-9999 */
  707.     if(isdigit((unsigned char)*p)) {
  708.         d->year = atoi(p);
  709.         if(d->year < 50)   
  710.           d->year += 2000;
  711.         else if(d->year < 100)
  712.           d->year += 1900;
  713.         while(*p && isdigit((unsigned char)*p))
  714.           p++;
  715.         while(*p && (*p == '-' || *p == ',' || isspace((unsigned char)*p)))
  716.           p++;
  717.     } else {
  718.         /* Something wierd, skip it and try to resynch */
  719.         while(*p && !isspace((unsigned char)*p) && *p != ',' && *p != '-')
  720.           p++;
  721.         while(*p && (isspace((unsigned char)*p) || *p == ',' || *p == '-'))
  722.           p++;
  723.     }
  724.  
  725.     /*-- Now get hours minutes, seconds and ignore tenths --*/
  726.     for(q = p; *q && isdigit((unsigned char)*q); q++);
  727.     if(*q == ':' && d->hour == -1) {
  728.         d->hour = atoi(p);
  729.         while(*p && *p != ':' && !isspace((unsigned char)*p))
  730.           p++;
  731.         if(*p == ':') {
  732.             p++;
  733.             d->minute = atoi(p);
  734.             while(*p && *p != ':' && !isspace((unsigned char)*p))
  735.               p++;
  736.             if(*p == ':') {
  737.                 p++;
  738.                 d->sec = atoi(p);
  739.                 while(*p && !isspace((unsigned char)*p))
  740.                   p++;
  741.             }
  742.         }
  743.     }
  744.     while(*p && isspace((unsigned char)*p))
  745.       p++;
  746.  
  747.  
  748.     /*-- The time zone --*/
  749.     d->hours_off_gmt = 0;
  750.     d->min_off_gmt = 0;
  751.     if(*p) {
  752.         if((*p == '+' || *p == '-')
  753.        && isdigit((unsigned char)p[1])
  754.        && isdigit((unsigned char)p[2])
  755.        && isdigit((unsigned char)p[3])
  756.        && isdigit((unsigned char)p[4])
  757.        && !isdigit((unsigned char)p[5])) {
  758.             char tmp[3];
  759.             d->min_off_gmt = d->hours_off_gmt = (*p == '+' ? 1 : -1);
  760.             p++;
  761.             tmp[0] = *p++;
  762.             tmp[1] = *p++;
  763.             tmp[2] = '\0';
  764.             d->hours_off_gmt *= atoi(tmp);
  765.             tmp[0] = *p++;
  766.             tmp[1] = *p++;
  767.             tmp[2] = '\0';
  768.             d->min_off_gmt *= atoi(tmp);
  769.         } else {
  770.         for(n = 0; known_zones[n].zone; n++)
  771.           if(struncmp(p, known_zones[n].zone, known_zones[n].len) == 0){
  772.           d->hours_off_gmt = (int) known_zones[n].hour_offset;
  773.           d->min_off_gmt   = (int) known_zones[n].min_offset;
  774.           break;
  775.           }
  776.         }
  777.     }
  778.     dprint(9, (debugfile,
  779.      "Parse date: \"%s\" to..  hours_off_gmt:%d  min_off_gmt:%d\n",
  780.                given_date, d->hours_off_gmt, d->min_off_gmt));
  781.     dprint(9, (debugfile,
  782.            "Parse date: wkday:%d  month:%d  year:%d  day:%d  hour:%d  min:%d  sec:%d\n",
  783.             d->wkday, d->month, d->year, d->day, d->hour, d->minute, d->sec));
  784. }
  785.  
  786.  
  787.  
  788. /*----------------------------------------------------------------------
  789.     Convert the given date to GMT
  790.  
  791.   Args -- d:  The date to be converted in place
  792.  ----*/
  793. void
  794. convert_to_gmt(d)
  795.     MESSAGECACHE *d;
  796. {
  797.     int minutes = d->minutes,            /* 0-59 */
  798.         hours    = d->hours,            /* 0-23 */
  799.     day    = d->day,            /* 1-31 */
  800.     month    = d->month,            /* 1-12 */
  801.     year    = d->year;            /* since 1969 */
  802.  
  803.     if(d->zhours == 0 && d->zminutes == 0)    /* no offset! */
  804.       return;
  805.  
  806.     if(d->zoccident){                /* adjust for distance */
  807.     minutes += d->zminutes;            /* ... west of GMT */
  808.     hours    += d->zhours;
  809.     }
  810.     else{
  811.     minutes -= d->zminutes;
  812.     hours    -= d->zhours;
  813.     }
  814.  
  815.     if(minutes < 0){                /* go to previous hour */
  816.     hours--;
  817.     minutes += 60;
  818.     }
  819.     else if(minutes > 59){            /* next hour */
  820.     hours++;
  821.     minutes -= 60;
  822.     }
  823.  
  824.     if(hours < 0){                /* previous day */
  825.     day--;
  826.     hours += 24;
  827.     }
  828.     else if(hours > 23){            /* next day */
  829.     day++;
  830.     hours -= 24;
  831.     }
  832.  
  833.     if(day < 0){                /* previous month */
  834.     if(--month <= 0){
  835.         month = 12;
  836.         year--;
  837.     }
  838.     day = days_in_month(month);
  839.     }
  840.     else if(day > days_in_month(month)){  /* next month */
  841.     if(++month > 12){
  842.         month = 0;
  843.         year++;
  844.     }
  845.     day = 1;
  846.     }
  847.  
  848.     d->zoccident = 0;
  849.     d->zhours    = 0;
  850.     d->zminutes  = 0;
  851.     d->year      = year;
  852.     d->month     = month;
  853.     d->day       = day;
  854.     d->hours     = hours;
  855.     d->minutes   = minutes;
  856. }
  857.  
  858.  
  859.  
  860. /*----------------------------------------------------------------------
  861.   The arguments are pointers to c-client MESSAGECACHE elts which have
  862.   had dates placed into them.
  863.   ----*/
  864. compare_dates(d1, d2)
  865.     MESSAGECACHE *d1, *d2;
  866. {
  867.     /*
  868.      * Check to see if the two dates are in different timezones, and
  869.      * convert to gmt if they are.
  870.      */
  871.     if(!(d1->zoccident == d2->zoccident &&
  872.          d1->zhours    == d2->zhours    &&
  873.          d1->zminutes  == d2->zminutes)){
  874.     convert_to_gmt(d1);
  875.     convert_to_gmt(d2);
  876.     }
  877.  
  878.     /* Now do the compare */
  879.     if(d1->year == d2->year)
  880.       if(d1->month == d2->month)
  881.         if(d1->day == d2->day)
  882.           if(d1->hours == d2->hours)
  883.             if(d1->minutes == d2->minutes)
  884.               if(d1->seconds == d2->seconds)
  885.         return 0;
  886.           else
  887.                 return((int)(d1->seconds - d2->seconds));
  888.         else
  889.               return((int)(d1->minutes - d2->minutes));
  890.       else
  891.             return((int)(d1->hours - d2->hours));
  892.     else
  893.           return((int)(d1->day - d2->day));
  894.       else
  895.         return((int)(d1->month - d2->month));
  896.     else
  897.       return((int)(d1->year - d2->year));
  898. }
  899.     
  900.  
  901. /*----------------------------------------------------------------------
  902.      Map some of the special characters into sensible strings for human
  903.    consumption.
  904.   ----*/
  905. char *
  906. pretty_command(c)
  907.      int c;
  908. {
  909.     static char  buf[10];
  910.     char    *s;
  911.  
  912.     switch(c){
  913.       case '\033'    : s = "ESC";        break;
  914.       case '\177'    : s = "DEL";        break;
  915.       case ctrl('I') : s = "TAB";        break;
  916.       case ctrl('J') : s = "LINEFEED";        break;
  917.       case ctrl('M') : s = "RETURN";        break;
  918.       case ctrl('Q') : s = "XON";        break;
  919.       case ctrl('S') : s = "XOFF";        break;
  920.       case KEY_UP    : s = "Up Arrow";        break;
  921.       case KEY_DOWN  : s = "Down Arrow";    break;
  922.       case KEY_RIGHT : s = "Right Arrow";    break;
  923.       case KEY_LEFT  : s = "Left Arrow";    break;
  924.       case KEY_PGUP  : s = "Prev Page";        break;
  925.       case KEY_PGDN  : s = "Next Page";        break;
  926.       case KEY_HOME  : s = "Home";        break;
  927.       case KEY_END   : s = "End";        break;
  928.       case KEY_DEL   : s = "Delete";        break; /* Not necessary DEL! */
  929.       case PF1         :
  930.       case PF2         :
  931.       case PF3         :
  932.       case PF4         :
  933.       case PF5         :
  934.       case PF6         :
  935.       case PF7         :
  936.       case PF8         :
  937.       case PF9         :
  938.       case PF10         :
  939.       case PF11         :
  940.       case PF12         :
  941.         sprintf(s = buf, "F%d", c - PF1 + 1);
  942.     break;
  943.  
  944.       default:
  945.     if(c < ' ')
  946.       sprintf(s = buf, "^%c", c + 'A' - 1);
  947.     else
  948.       sprintf(s = buf, "%c", c);
  949.  
  950.     break;
  951.     }
  952.  
  953.     return(s);
  954. }
  955.         
  956.     
  957.  
  958. /*----------------------------------------------------------------------
  959.      Create a little string of blanks of the specified length.
  960.    Max n is 511.
  961.   ----*/
  962. char *
  963. repeat_char(n, c)
  964.      int  n;
  965.      int  c;
  966. {
  967.     static char bb[512];
  968.     if(n > sizeof(bb))
  969.        n = sizeof(bb) - 1;
  970.     bb[n--] = '\0';
  971.     while(n >= 0)
  972.       bb[n--] = c;
  973.     return(bb);
  974. }
  975.  
  976.  
  977. /*----------------------------------------------------------------------
  978.         Turn a number into a string with comma's
  979.  
  980.    Args: number -- The long to be turned into a string. 
  981.  
  982.   Result: pointer to static string representing number with commas
  983.   ---*/
  984. char *
  985. comatose(number) 
  986.     long number;
  987. {
  988. #ifdef    DOS
  989.     static char buf[16];        /* no numbers > 1 trillion! */
  990.     char *b;
  991.     short i;
  992.  
  993.     if(!number)
  994.     return("0");
  995.  
  996.     if(number < 0x7FFFFFFFL){        /* largest DOS signed long */
  997.         buf[15] = '\0';
  998.         b = &buf[14];
  999.         i = 2;
  1000.     while(number){
  1001.          *b-- = (number%10) + '0';
  1002.         if((number /= 10) && i-- == 0 ){
  1003.         *b-- = ',';
  1004.         i = 2;
  1005.         }
  1006.     }
  1007.     }
  1008.     else
  1009.       return("Number too big!");        /* just fits! */
  1010.  
  1011.     return(++b);
  1012. #else
  1013.     long        i, x, done_one;
  1014.     static char buf[100];
  1015.     char       *b;
  1016.  
  1017.     dprint(9, (debugfile, "comatose(%ld) returns:", number));
  1018.     if(number == 0){
  1019.         strcpy(buf, "0");
  1020.         return(buf);
  1021.     }
  1022.     
  1023.     done_one = 0;
  1024.     b = buf;
  1025.     for(i = 1000000000; i >= 1; i /= 1000) {
  1026.     x = number / i;
  1027.     number = number % i;
  1028.     if(x != 0 || done_one) {
  1029.         if(b != buf)
  1030.           *b++ = ',';
  1031.         sprintf(b, done_one ? "%03ld" : "%d", x);
  1032.         b += strlen(b);
  1033.         done_one = 1;
  1034.     }
  1035.     }
  1036.     *b = '\0';
  1037.  
  1038.     dprint(9, (debugfile, "\"%s\"\n", buf));
  1039.  
  1040.     return(buf);
  1041. #endif    /* DOS */
  1042. }
  1043.  
  1044.  
  1045.  
  1046. /*----------------------------------------------------------------------
  1047.    Format number as amount of bytes, appending Kb, Mb, Gb, bytes
  1048.  
  1049.   Args: bytes -- number of bytes to format
  1050.  
  1051.  Returns pointer to static string. The numbers are divided to produce a 
  1052. nice string with precision of about 2-4 digits
  1053.     ----*/
  1054. char *
  1055. byte_string(bytes)
  1056.      long bytes;
  1057. {
  1058.     char       *a, aa[5];
  1059.     char       *abbrevs = "GMK";
  1060.     long        i, ones, tenths;
  1061.     static char string[10];
  1062.  
  1063.     ones   = 0L;
  1064.     tenths = 0L;
  1065.  
  1066.     if(bytes == 0L){
  1067.         strcpy(string, "0 bytes");
  1068.     } else {
  1069.         for(a = abbrevs, i = 1000000000; i >= 1; i /= 1000, a++) {
  1070.             if(bytes > i) {
  1071.                 ones = bytes/i;
  1072.                 if(ones < 10L && i > 10L)
  1073.                   tenths = (bytes - (ones * i)) / (i / 10L);
  1074.                 break;
  1075.             }
  1076.         }
  1077.     
  1078.         aa[0] = *a;  aa[1] = '\0'; 
  1079.     
  1080.         if(tenths == 0)
  1081.           sprintf(string, "%ld%s%s", ones, aa, *a ? "B" : "bytes");
  1082.         else
  1083.           sprintf(string, "%ld.%ld%s%s", ones, tenths, aa, *a ? "B" : "bytes");
  1084.     }
  1085.  
  1086.     return(string);
  1087. }
  1088.  
  1089.  
  1090.  
  1091. /*----------------------------------------------------------------------
  1092.     Print a string corresponding to the number given:
  1093.       1st, 2nd, 3rd, 105th, 92342nd....
  1094.  ----*/
  1095.  
  1096. char *
  1097. enth_string(i)
  1098.      int i;
  1099. {
  1100.     static char enth[10];
  1101.  
  1102.     switch (i % 10) {
  1103.         
  1104.       case 1:
  1105.         if( (i % 100 ) == 11)
  1106.           sprintf(enth,"%dth", i);
  1107.         else
  1108.           sprintf(enth,"%dst", i);
  1109.         break;
  1110.  
  1111.       case 2:
  1112.         if ((i % 100) == 12)
  1113.           sprintf(enth, "%dth",i);
  1114.         else
  1115.           sprintf(enth, "%dnd",i);
  1116.         break;
  1117.  
  1118.       case 3:
  1119.         if(( i % 100) == 13)
  1120.           sprintf(enth, "%dth",i);
  1121.         else
  1122.           sprintf(enth, "%drd",i);
  1123.         break;
  1124.  
  1125.       default:
  1126.         sprintf(enth,"%dth",i);
  1127.         break;
  1128.     }
  1129.     return(enth);
  1130. }
  1131.  
  1132.  
  1133. char *
  1134. long2string(l)
  1135.      long l;
  1136. {
  1137.     static char string[20];
  1138.     sprintf(string, "%ld", l);
  1139.     return(string);
  1140. }
  1141.  
  1142. char *
  1143. int2string(i)
  1144.      int i;
  1145. {
  1146.     static char string[20];
  1147.     sprintf(string, "%d", i);
  1148.     return(string);
  1149. }
  1150.  
  1151.  
  1152. /*
  1153.  * strtoval - convert the given string to a positive integer.
  1154.  */
  1155. char *
  1156. strtoval(s, val, minmum, maxmum, errbuf, varname)
  1157.     char *s;
  1158.     int  *val;
  1159.     int   minmum, maxmum;
  1160.     char *errbuf, *varname;
  1161. {
  1162.     int   i = 0;
  1163.     char *p = s, *errstr = NULL;
  1164.  
  1165.     removing_leading_white_space(p);
  1166.     removing_trailing_white_space(p);
  1167.     for(; *p; p++)
  1168.       if(isdigit((unsigned char)*p)){
  1169.       i = (i * 10) + (*p - '0');
  1170.       }
  1171.       else{
  1172.       sprintf(errstr = errbuf,
  1173.           "Non-numeric value ('%c' in \"%.8s\") in %s. Using \"%d\"",
  1174.           *p, s, varname, *val);
  1175.       return(errbuf);
  1176.       }
  1177.  
  1178.     /* range describes acceptable values */
  1179.     if(maxmum > minmum && (i < minmum || i > maxmum))
  1180.       sprintf(errstr = errbuf,
  1181.           "%s of %d not supported (M%s %d). Using \"%d\"",
  1182.           varname, i, (i > maxmum) ? "ax" : "in",
  1183.           (i > maxmum) ? maxmum : minmum, *val);
  1184.     /* range describes unacceptable values */
  1185.     else if(minmum > maxmum && !(i < maxmum || i > minmum))
  1186.       sprintf(errstr = errbuf, "%s of %d not supported. Using \"%d\"",
  1187.           varname, i, *val);
  1188.     else
  1189.       *val = i;
  1190.  
  1191.     return(errstr);
  1192. }
  1193.  
  1194.  
  1195. /*
  1196.  *  Function to parse the given string into two space-delimited fields
  1197.  */
  1198. void
  1199. get_pair(string, label, value, firstws)
  1200.     char *string, **label, **value;
  1201.     int   firstws;
  1202. {
  1203.     char *p, *token = NULL;
  1204.     int      quoted = 0;
  1205.  
  1206.     *label = *value = NULL;
  1207.     for(p = string; *p;){
  1208.     if(*p == '"')                /* quoted label? */
  1209.       quoted = (quoted) ? 0 : 1;
  1210.  
  1211.     if(*p == '\\' && *(p+1) == '"')        /* escaped quote? */
  1212.       p++;                    /* skip it... */
  1213.  
  1214.     if(isspace((unsigned char)*p) && !quoted){    /* if space,  */
  1215.         while(*++p && isspace((unsigned char)*p))    /* move past it */
  1216.           ;
  1217.  
  1218.         if(!firstws || !token)
  1219.           token = p;            /* remember start of text */
  1220.     }
  1221.     else
  1222.       p++;
  1223.     }
  1224.  
  1225.     if(token){
  1226.     *label = p = (char *)fs_get(((token - string) + 1) * sizeof(char));
  1227.     for(; string < token ; string++){
  1228.         if(*string == '\\' && *(string+1) == '"')
  1229.           *p++ = *++string;
  1230.         else if(*string != '"')
  1231.           *p++ = *string;
  1232.     }
  1233.  
  1234.     *p = '\0';                /* tie off nickname */
  1235.     removing_trailing_white_space(*label);
  1236.     *value = cpystr(token);
  1237.     }
  1238.     else
  1239.       *value = cpystr(string);
  1240. }
  1241.  
  1242.  
  1243. /*
  1244.  * Convert a 1 or 2-digit hex string into an 8-bit character.
  1245.  * The input string should be checked for hex'ness before calling this.
  1246.  * Only the first two characters of s will be used, and it is ok not
  1247.  * to null-terminate it.
  1248.  */
  1249. int
  1250. read_hex(s)
  1251.     char *s;
  1252. {
  1253.     register int i, c, j;
  1254.  
  1255.     i = 0;
  1256.     for(j = 0; j < 2 && *s && isxdigit((unsigned char)*s); s++,j++){
  1257.     c = (unsigned char)(*s);
  1258.     if(isupper((unsigned char)c))
  1259.       c = tolower((unsigned char)c);
  1260.  
  1261.     i = i*16 + (isdigit((unsigned char)c)  ? c - '0'
  1262.                            : c - 'a' + 10);
  1263.     }
  1264.  
  1265.     return(i);
  1266. }
  1267.  
  1268.  
  1269. /*
  1270.  * Convert a 1, 2, or 3-digit octal string into an 8-bit character.
  1271.  * Only the first three characters of s will be used, and it is ok not
  1272.  * to null-terminate it.
  1273.  */
  1274. int
  1275. read_octal(s)
  1276.     char *s;
  1277. {
  1278.     register int i, c, j;
  1279.  
  1280.     i = 0;
  1281.     for(j = 0; j < 3 && *s && isdigit((unsigned char)(*s)); s++,j++)
  1282.       i = i*8 + (int)(unsigned char)(*s) - '0';
  1283.  
  1284.     return(i);
  1285. }
  1286.  
  1287.  
  1288. /*
  1289.  * Given a character c, put the 3-digit ascii octal value of that char
  1290.  * in the 2nd argument, which must be at least 3 in length.
  1291.  */
  1292. void
  1293. char_to_octal_triple(c, octal)
  1294.     int   c;
  1295.     char *octal;
  1296. {
  1297.     c &= 0xff;
  1298.  
  1299.     octal[2] = (c % 8) + '0';
  1300.     c /= 8;
  1301.     octal[1] = (c % 8) + '0';
  1302.     c /= 8;
  1303.     octal[0] = c + '0';
  1304. }
  1305.  
  1306.  
  1307. /*
  1308.  * Given a character c, put the 2-digit ascii hex value of that char
  1309.  * in the 2nd argument, which must be at least 2 in length.
  1310.  */
  1311. void
  1312. char_to_hex_pair(c, hex)
  1313.     int   c;
  1314.     char *hex;
  1315. {
  1316.     int d;
  1317.  
  1318.     c &= 0xff;
  1319.     d = c % 16;
  1320.     hex[1] = (d < 10) ? ('0' + d) : ('a' + d - 10);
  1321.     c /= 16;
  1322.     hex[0] = (c < 10) ? ('0' + c) : ('a' + c - 10);
  1323. }
  1324.  
  1325.  
  1326. /*
  1327.  * Convert in memory string s to a C-style string, with backslash escapes
  1328.  * like they're used in C character constants.
  1329.  *
  1330.  * Returns allocated C string version of s.
  1331.  */
  1332. char *
  1333. string_to_cstring(s)
  1334.     char *s;
  1335. {
  1336.     char *b, *p;
  1337.     int   n, i;
  1338.  
  1339.     if(!s)
  1340.       return(cpystr(""));
  1341.  
  1342.     n = 20;
  1343.     b = (char *)fs_get((n+1) * sizeof(char));
  1344.     p  = b;
  1345.     *p = '\0';
  1346.     i  = 0;
  1347.  
  1348.     while(*s){
  1349.     if(i + 4 > n){
  1350.         /*
  1351.          * The output string may overflow the output buffer.
  1352.          * Make more room.
  1353.          */
  1354.         n += 20;
  1355.         fs_resize((void **)&b, (n+1) * sizeof(char));
  1356.         p = &b[i];
  1357.     }
  1358.     else{
  1359.         switch(*s){
  1360.           case '\n':
  1361.         *p++ = '\\';
  1362.         *p++ = 'n';
  1363.         i += 2;
  1364.         break;
  1365.  
  1366.           case '\r':
  1367.         *p++ = '\\';
  1368.         *p++ = 'r';
  1369.         i += 2;
  1370.         break;
  1371.  
  1372.           case '\t':
  1373.         *p++ = '\\';
  1374.         *p++ = 't';
  1375.         i += 2;
  1376.         break;
  1377.  
  1378.           case '\b':
  1379.         *p++ = '\\';
  1380.         *p++ = 'b';
  1381.         i += 2;
  1382.         break;
  1383.  
  1384.           case '\f':
  1385.         *p++ = '\\';
  1386.         *p++ = 'f';
  1387.         i += 2;
  1388.         break;
  1389.  
  1390.           case '\\':
  1391.         *p++ = '\\';
  1392.         *p++ = '\\';
  1393.         i += 2;
  1394.         break;
  1395.  
  1396.           default:
  1397.         if(*s >= SPACE && *s <= '~'){
  1398.             *p++ = *s;
  1399.             i++;
  1400.         }
  1401.         else{  /* use octal output */
  1402.             *p++ = '\\';
  1403.             char_to_octal_triple(*s, p);
  1404.             p += 3;
  1405.             i += 4;
  1406.         }
  1407.  
  1408.         break;
  1409.         }
  1410.  
  1411.         s++;
  1412.     }
  1413.     }
  1414.  
  1415.     *p = '\0';
  1416.     return(b);
  1417. }
  1418.  
  1419.  
  1420. /*
  1421.  * Convert C-style string, with backslash escapes, into a hex string, two
  1422.  * hex digits per character.
  1423.  *
  1424.  * Returns allocated hexstring version of s.
  1425.  */
  1426. char *
  1427. cstring_to_hexstring(s)
  1428.     char *s;
  1429. {
  1430.     char *b, *p;
  1431.     int   n, i, c;
  1432.  
  1433.     if(!s)
  1434.       return(cpystr(""));
  1435.  
  1436.     n = 20;
  1437.     b = (char *)fs_get((n+1) * sizeof(char));
  1438.     p  = b;
  1439.     *p = '\0';
  1440.     i  = 0;
  1441.  
  1442.     while(*s){
  1443.     if(i + 2 > n){
  1444.         /*
  1445.          * The output string may overflow the output buffer.
  1446.          * Make more room.
  1447.          */
  1448.         n += 20;
  1449.         fs_resize((void **)&b, (n+1) * sizeof(char));
  1450.         p = &b[i];
  1451.     }
  1452.     else{
  1453.         if(*s == '\\'){
  1454.         s++;
  1455.         switch(*s){
  1456.           case 'n':
  1457.             c = '\n';
  1458.             char_to_hex_pair(c, p);
  1459.             i += 2;
  1460.             p += 2;
  1461.             s++;
  1462.             break;
  1463.  
  1464.           case 'r':
  1465.             c = '\r';
  1466.             char_to_hex_pair(c, p);
  1467.             i += 2;
  1468.             p += 2;
  1469.             s++;
  1470.             break;
  1471.  
  1472.           case 't':
  1473.             c = '\t';
  1474.             char_to_hex_pair(c, p);
  1475.             i += 2;
  1476.             p += 2;
  1477.             s++;
  1478.             break;
  1479.  
  1480.           case 'v':
  1481.             c = '\v';
  1482.             char_to_hex_pair(c, p);
  1483.             i += 2;
  1484.             p += 2;
  1485.             s++;
  1486.             break;
  1487.  
  1488.           case 'b':
  1489.             c = '\b';
  1490.             char_to_hex_pair(c, p);
  1491.             i += 2;
  1492.             p += 2;
  1493.             s++;
  1494.             break;
  1495.  
  1496.           case 'f':
  1497.             c = '\f';
  1498.             char_to_hex_pair(c, p);
  1499.             i += 2;
  1500.             p += 2;
  1501.             s++;
  1502.             break;
  1503.  
  1504.           case 'a':
  1505.             c = '\007';
  1506.             char_to_hex_pair(c, p);
  1507.             i += 2;
  1508.             p += 2;
  1509.             s++;
  1510.             break;
  1511.  
  1512.           case '\\':
  1513.             c = '\\';
  1514.             char_to_hex_pair(c, p);
  1515.             i += 2;
  1516.             p += 2;
  1517.             s++;
  1518.             break;
  1519.  
  1520.           case '?':
  1521.             c = '?';
  1522.             char_to_hex_pair(c, p);
  1523.             i += 2;
  1524.             p += 2;
  1525.             s++;
  1526.             break;
  1527.  
  1528.           case '\'':
  1529.             c = '\'';
  1530.             char_to_hex_pair(c, p);
  1531.             i += 2;
  1532.             p += 2;
  1533.             s++;
  1534.             break;
  1535.  
  1536.           case '\"':
  1537.             c = '\"';
  1538.             char_to_hex_pair(c, p);
  1539.             i += 2;
  1540.             p += 2;
  1541.             s++;
  1542.             break;
  1543.  
  1544.           case 0: /* reached end of s too early */
  1545.             c = 0;
  1546.             char_to_hex_pair(c, p);
  1547.             i += 2;
  1548.             p += 2;
  1549.             s++;
  1550.             break;
  1551.  
  1552.           /* hex number */
  1553.           case 'x':
  1554.             s++;
  1555.             if(isxdigit((unsigned char)*s)){
  1556.             c = read_hex(s);
  1557.             s++;
  1558.             if(isxdigit((unsigned char)*s))
  1559.               s++;
  1560.             }
  1561.             else
  1562.               c = 0;
  1563.  
  1564.             char_to_hex_pair(c, p);
  1565.             i += 2;
  1566.             p += 2;
  1567.  
  1568.             break;
  1569.  
  1570.           /* octal number */
  1571.           default:
  1572.             if(isdigit((unsigned char)*s)){
  1573.             c = read_octal(s);
  1574.             s++;
  1575.             if(isdigit((unsigned char)*s)){
  1576.                 s++;
  1577.                 if(isdigit((unsigned char)*s))
  1578.                   s++;
  1579.             }
  1580.             }
  1581.             else
  1582.               c = 0;
  1583.  
  1584.             char_to_hex_pair(c, p);
  1585.             i += 2;
  1586.             p += 2;
  1587.  
  1588.             break;
  1589.         }
  1590.         }
  1591.         else{
  1592.         char_to_hex_pair(*s, p);
  1593.         i += 2;
  1594.         p += 2;
  1595.         s++;
  1596.         }
  1597.     }
  1598.     }
  1599.  
  1600.     *p = '\0';
  1601.     return(b);
  1602. }
  1603.  
  1604.  
  1605. /*
  1606.  * Returns non-zero if dir is a prefix of path.
  1607.  *         zero     if dir is not a prefix of path, or if dir is empty.
  1608.  */
  1609. int
  1610. in_dir(dir, path)
  1611.     char *dir;
  1612.     char *path;
  1613. {
  1614.     return(*dir ? !strncmp(dir, path, strlen(dir)) : 0);
  1615. }
  1616.  
  1617.  
  1618.  
  1619. /*
  1620.  * Return pointer to given string after turning off all hi-order bits
  1621.  */
  1622. char *
  1623. bitstrip(s)
  1624.     char *s;
  1625. {
  1626.     register char *p = s;
  1627.  
  1628.     while(*p &= 0x7f)
  1629.       p++;
  1630.  
  1631.     return(s);
  1632. }
  1633.  
  1634.  
  1635.  
  1636. /*
  1637.  *  * * * * * * * *      RFC 1522 support routines      * * * * * * * *
  1638.  *
  1639.  *   RFC 1522 support is *very* loosely based on code contributed
  1640.  *   by Lars-Erik Johansson <lej@cdg.chalmers.se>.  Thanks to Lars-Erik,
  1641.  *   and appologies for taking such liberties with his code.
  1642.  */
  1643.  
  1644.  
  1645. #define    RFC1522_INIT    "=?"
  1646. #define    RFC1522_INIT_L    2
  1647. #define RFC1522_TERM    "?="
  1648. #define    RFC1522_TERM_L    2
  1649. #define    RFC1522_DLIM    "?"
  1650. #define    RFC1522_DLIM_L    1
  1651. #define    RFC1522_MAXW    75
  1652. #define    ESPECIALS    "()<>@,;:\"/[]?.="
  1653. #define    RFC1522_OVERHEAD(S)    (RFC1522_INIT_L + RFC1522_TERM_L +    \
  1654.                  (2 * RFC1522_DLIM_L) + strlen(S) + 1);
  1655. #define    RFC1522_ENC_CHAR(C)    (((C) & 0x80) || !rfc1522_valtok(C)    \
  1656.                  || (C) == '_' )
  1657.  
  1658.  
  1659. int           rfc1522_token PROTO((char *, int (*) PROTO((int)), char *,
  1660.                     char **));
  1661. int           rfc1522_valtok PROTO((int));
  1662. int           rfc1522_valenc PROTO((int));
  1663. int           rfc1522_valid PROTO((char *, char **, char **, char **,
  1664.                     char **));
  1665. char          *rfc1522_8bit PROTO((void *, int));
  1666. char          *rfc1522_binary PROTO((void *, int));
  1667. unsigned char *rfc1522_encoded_word PROTO((unsigned char *, int, char *));
  1668.  
  1669.  
  1670. /*
  1671.  * rfc1522_decode - decode the given source string ala RFC 1522,
  1672.  *            IF NECESSARY, into the given destination buffer.
  1673.  *            Don't bother copying if it turns out decoding
  1674.  *            isn't necessary.
  1675.  *
  1676.  * Returns: pointer to either the destination buffer containing the
  1677.  *        decoded text, or a pointer to the source buffer if there was
  1678.  *        no reason to decode it.
  1679.  */
  1680. unsigned char *
  1681. rfc1522_decode(d, s, charset)
  1682.     unsigned char  *d;
  1683.     char       *s;
  1684.     char      **charset;
  1685. {
  1686.     unsigned char *rv = NULL, *p;
  1687.     char      *start = s, *sw, *cset, *enc, *txt, *ew, **q;
  1688.     unsigned long  l;
  1689.     int           i;
  1690.  
  1691.     *d = '\0';                    /* init destination */
  1692.     if(charset)
  1693.       *charset = NULL;
  1694.  
  1695.     while(s && (sw = strstr(s, RFC1522_INIT))){
  1696.     if(!rv)
  1697.       rv = d;                /* remember start of dest */
  1698.  
  1699.     /* validate the rest of the encoded-word */
  1700.     if(rfc1522_valid(sw, &cset, &enc, &txt, &ew)){
  1701.         /* copy everything between s and sw to destination */
  1702.         for(i = 0; &s[i] < sw; i++)
  1703.           if(!isspace((unsigned char)s[i])){ /* if some non-whitespace */
  1704.           while(s < sw)
  1705.             *d++ = (unsigned char) *s++;
  1706.  
  1707.           break;
  1708.           }
  1709.  
  1710.         enc[-1] = txt[-1] = ew[0] = '\0';    /* tie off token strings */
  1711.  
  1712.         /* Insert text explaining charset if we don't know what it is */
  1713.         if((!ps_global->VAR_CHAR_SET
  1714.         || strucmp((char *) cset, ps_global->VAR_CHAR_SET))
  1715.            && strucmp((char *) cset, "US-ASCII")){
  1716.         dprint(5, (debugfile, "RFC1522_decode: charset mismatch: %s\n",
  1717.                cset));
  1718.         if(charset){
  1719.             if(!*charset)        /* only write first charset */
  1720.               *charset = cpystr(cset);
  1721.         }
  1722.         else{
  1723.             *d++ = '[';
  1724.             sstrcpy((char **) &d, cset);
  1725.             *d++ = ']';
  1726.             *d++ = SPACE;
  1727.         }
  1728.         }
  1729.  
  1730.         /* based on encoding, write the encoded text to output buffer */
  1731.         switch(*enc){
  1732.           case 'Q' :            /* 'Q' encoding */
  1733.           case 'q' :
  1734.         /* special hocus-pocus to deal with '_' exception, too bad */
  1735.         for(l = 0L, i = 0; txt[l]; l++)
  1736.           if(txt[l] == '_')
  1737.             i++;
  1738.  
  1739.         if(i){
  1740.             q = (char **) fs_get((i + 1) * sizeof(char *));
  1741.             for(l = 0L, i = 0; txt[l]; l++)
  1742.               if(txt[l] == '_'){
  1743.               q[i++] = &txt[l];
  1744.               txt[l] = SPACE;
  1745.               }
  1746.  
  1747.             q[i] = NULL;
  1748.         }
  1749.         else
  1750.           q = NULL;
  1751.  
  1752.         if(p = rfc822_qprint((unsigned char *)txt, strlen(txt), &l)){
  1753.             strcpy((char *) d, (char *) p);
  1754.             fs_give((void **)&p);    /* free encoded buf */
  1755.             d += l;            /* advance dest ptr to EOL */
  1756.         }
  1757.         else
  1758.           goto bogus;
  1759.  
  1760.         if(q){                /* restore underscores */
  1761.             for(i = 0; q[i]; i++)
  1762.               *(q[i]) = '_';
  1763.  
  1764.             fs_give((void **)&q);
  1765.         }
  1766.  
  1767.         break;
  1768.  
  1769.           case 'B' :            /* 'B' encoding */
  1770.           case 'b' :
  1771.         if(p = rfc822_base64((unsigned char *) txt, strlen(txt), &l)){
  1772.             strcpy((char *) d, (char *) p);
  1773.             fs_give((void **)&p);    /* free encoded buf */
  1774.             d += l;            /* advance dest ptr to EOL */
  1775.         }
  1776.         else
  1777.           goto bogus;
  1778.  
  1779.         break;
  1780.  
  1781.           default:
  1782.         sstrcpy((char **) &d, txt);
  1783.         dprint(1, (debugfile, "RFC1522_decode: Unknown ENCODING: %s\n",
  1784.                enc));
  1785.         break;
  1786.         }
  1787.  
  1788.         /* restore trompled source string */
  1789.         enc[-1] = txt[-1] = '?';
  1790.         ew[0]   = RFC1522_TERM[0];
  1791.  
  1792.         /* advance s to start of text after encoded-word */
  1793.         s = ew + RFC1522_TERM_L;
  1794.     }
  1795.     else{
  1796.         /* found intro, but bogus data followed */
  1797.         strncpy((char *) d, s, (int) (l = (sw - s) + RFC1522_INIT_L));
  1798.         *(d += l) = '\0';            /* advance d, tie off text */
  1799.         s += l;                /* advance s beyond intro */
  1800.     }
  1801.     }
  1802.  
  1803.     if(rv && *s)                /* copy remaining text */
  1804.       strcat((char *) rv, s);
  1805.  
  1806. /* BUG: MUST do code page mapping under DOS after decoding */
  1807.  
  1808.     return(rv ? rv : (unsigned char *) start);
  1809.  
  1810.   bogus:
  1811.     dprint(1, (debugfile, "RFC1522_decode: BOGUS INPUT: -->%s<--\n", start));
  1812.     return((unsigned char *) start);
  1813. }
  1814.  
  1815.  
  1816. /*
  1817.  * rfc1522_token - scan the given source line up to the end_str making
  1818.  *           sure all subsequent chars are "valid" leaving endp
  1819.  *           a the start of the end_str.
  1820.  * Returns: TRUE if we got a valid token, FALSE otherwise
  1821.  */
  1822. int
  1823. rfc1522_token(s, valid, end_str, endp)
  1824.     char  *s;
  1825.     int     (*valid) PROTO((int));
  1826.     char  *end_str;
  1827.     char **endp;
  1828. {
  1829.     while(*s){
  1830.     if((char) *s == *end_str        /* test for matching end_str */
  1831.        && ((end_str[1])
  1832.             ? !strncmp((char *)s + 1, end_str + 1, strlen(end_str + 1))
  1833.             : 1)){
  1834.         *endp = s;
  1835.         return(TRUE);
  1836.     }
  1837.  
  1838.     if(!(*valid)(*s++))            /* test for valid char */
  1839.       break;
  1840.     }
  1841.  
  1842.     return(FALSE);
  1843. }
  1844.  
  1845.  
  1846. /*
  1847.  * rfc1522_valtok - test for valid character in the RFC 1522 encoded
  1848.  *            word's charset and encoding fields.
  1849.  */
  1850. int
  1851. rfc1522_valtok(c)
  1852.     int c;
  1853. {
  1854.     return(!(c == SPACE || iscntrl(c & 0x7f) || strindex(ESPECIALS, c)));
  1855. }
  1856.  
  1857.  
  1858. /*
  1859.  * rfc1522_valenc - test for valid character in the RFC 1522 encoded
  1860.  *            word's encoded-text field.
  1861.  */
  1862. int
  1863. rfc1522_valenc(c)
  1864.     int c;
  1865. {
  1866.     return(!(c == '?' || c == SPACE) && isprint((unsigned char)c));
  1867. }
  1868.  
  1869.  
  1870. /*
  1871.  * rfc1522_valid - validate the given string as to it's rfc1522-ness
  1872.  */
  1873. int
  1874. rfc1522_valid(s, charset, enc, txt, endp)
  1875.     char  *s;
  1876.     char **charset;
  1877.     char **enc;
  1878.     char **txt;
  1879.     char **endp;
  1880. {
  1881.     char *c, *e, *t, *p;
  1882.     int   rv;
  1883.  
  1884.     rv = rfc1522_token(c = s+RFC1522_INIT_L, rfc1522_valtok, RFC1522_DLIM, &e)
  1885.        && rfc1522_token(++e, rfc1522_valtok, RFC1522_DLIM, &t)
  1886.        && rfc1522_token(++t, rfc1522_valenc, RFC1522_TERM, &p)
  1887.        && p - s <= RFC1522_MAXW;
  1888.  
  1889.     if(charset)
  1890.       *charset = c;
  1891.  
  1892.     if(enc)
  1893.       *enc = e;
  1894.  
  1895.     if(txt)
  1896.       *txt = t;
  1897.  
  1898.     if(endp)
  1899.       *endp = p;
  1900.  
  1901.     return(rv);
  1902. }
  1903.  
  1904.  
  1905. /*
  1906.  * rfc1522_encode - encode the given source string ala RFC 1522,
  1907.  *            IF NECESSARY, into the given destination buffer.
  1908.  *            Don't bother copying if it turns out encoding
  1909.  *            isn't necessary.
  1910.  *
  1911.  * Returns: pointer to either the destination buffer containing the
  1912.  *        encoded text, or a pointer to the source buffer if we didn't
  1913.  *          have to encode anything.
  1914.  */
  1915. char *
  1916. rfc1522_encode(d, s, charset)
  1917.     char      *d;
  1918.     unsigned char *s;
  1919.     char      *charset;
  1920. {
  1921.     unsigned char *p, *q;
  1922.     char       enc;
  1923.     int           n, l;
  1924.  
  1925.     if(!(s && charset))
  1926.       return((char *) s);
  1927.  
  1928.     /* look for a reason to encode */
  1929.     for(p = s, n = 0; *p; p++)
  1930.       if((*p) & 0x80){
  1931.       n++;
  1932.       }
  1933.       else if(*p == RFC1522_INIT[0]
  1934.           && !strncmp((char *) p, RFC1522_INIT, RFC1522_INIT_L)){
  1935.       if(rfc1522_valid((char *) p, NULL, NULL, NULL, (char **) &q))
  1936.         p = q + RFC1522_TERM_L - 1;        /* advance past encoded gunk */
  1937.       else
  1938.         n++;
  1939.       }
  1940.       else if(*p == ESCAPE && match_escapes((char *)(p+1))){
  1941.       n++;
  1942.       }
  1943.  
  1944.     if(n){                    /* found, encoding to do */
  1945.     char *rv  = d, *t,
  1946.           enc = (n > (2 * (p - s)) / 3) ? 'B' : 'Q';
  1947.     int   slen;
  1948.  
  1949.     while(*s){
  1950.         sstrcpy(&d, RFC1522_INIT);        /* insert intro header, */
  1951.         sstrcpy(&d, charset);        /* character set tag, */
  1952.         sstrcpy(&d, RFC1522_DLIM);        /* and encoding flavor */
  1953.         *d++ = enc;
  1954.         sstrcpy(&d, RFC1522_DLIM);
  1955.  
  1956.         /*
  1957.          * feed lines to encoder such that they're guaranteed
  1958.          * less than RFC1522_MAXW.
  1959.          */
  1960.         p = rfc1522_encoded_word(s, enc, charset);
  1961.         if(enc == 'B')            /* insert encoded data */
  1962.           sstrcpy(&d, t = rfc1522_binary(s, p - s));
  1963.         else                /* 'Q' encoding */
  1964.           sstrcpy(&d, t = rfc1522_8bit(s, p - s));
  1965.  
  1966.         sstrcpy(&d, RFC1522_TERM);        /* insert terminator */
  1967.         fs_give((void **) &t);
  1968.         if(*p)                /* more src string follows */
  1969.           sstrcpy(&d, "\015\012 ");    /* insert continuation line */
  1970.  
  1971.         s = p;                /* advance s */
  1972.     }
  1973.  
  1974.     return(rv);
  1975.     }
  1976.     else
  1977.       return((char *) s);            /* no work for us here */
  1978. }
  1979.  
  1980.  
  1981.  
  1982. /*
  1983.  * rfc1522_encoded_word -- cut given string into max length encoded word
  1984.  *
  1985.  * Return: pointer into 's' such that the encoded 's' is no greater
  1986.  *       than RFC1522_MAXW
  1987.  *
  1988.  *  NOTE: this line break code is NOT cognizant of any SI/SO
  1989.  *  charset requirements nor similar strategies using escape
  1990.  *  codes.  Hopefully this will matter little and such
  1991.  *  representation strategies don't also include 8bit chars.
  1992.  */
  1993. unsigned char *
  1994. rfc1522_encoded_word(s, enc, charset)
  1995.     unsigned char *s;
  1996.     int           enc;
  1997.     char      *charset;
  1998. {
  1999.     int goal = RFC1522_MAXW - RFC1522_OVERHEAD(charset);
  2000.  
  2001.     if(enc == 'B')            /* base64 encode */
  2002.       for(goal = ((goal / 4) * 3) - 2; goal && *s; goal--, s++)
  2003.     ;
  2004.     else                /* special 'Q' encoding */
  2005.       for(; goal && *s; s++)
  2006.     if((goal -= RFC1522_ENC_CHAR(*s) ? 3 : 1) < 0)
  2007.       break;
  2008.  
  2009.     return(s);
  2010. }
  2011.  
  2012.  
  2013.  
  2014. /*
  2015.  * rfc1522_8bit -- apply RFC 1522 'Q' encoding to the given 8bit buffer
  2016.  *
  2017.  * Return: alloc'd buffer containing encoded string
  2018.  */
  2019. char *
  2020. rfc1522_8bit(src, slen)
  2021.     void *src;
  2022.     int   slen;
  2023. {
  2024.     static char *hex = "0123456789ABCDEF";
  2025.     char *ret = (char *) fs_get ((size_t) (3*slen + 2));
  2026.     char *d = ret;
  2027.     unsigned char c;
  2028.     unsigned char *s = (unsigned char *) src;
  2029.  
  2030.     while (slen--) {                /* for each character */
  2031.     if (((c = *s++) == '\015') && (*s == '\012') && slen) {
  2032.         *d++ = '\015';            /* true line break */
  2033.         *d++ = *s++;
  2034.         slen--;
  2035.     }
  2036.     else if(c == SPACE){            /* special encoding case */
  2037.         *d++ = '_';
  2038.     }
  2039.     else if(RFC1522_ENC_CHAR(c)){
  2040.         *d++ = '=';                /* quote character */
  2041.         *d++ = hex[c >> 4];            /* high order 4 bits */
  2042.         *d++ = hex[c & 0xf];        /* low order 4 bits */
  2043.     }
  2044.     else
  2045.       *d++ = (char) c;            /* ordinary character */
  2046.     }
  2047.  
  2048.     *d = '\0';                    /* tie off destination */
  2049.     return(ret);
  2050. }
  2051.  
  2052.  
  2053. /*
  2054.  * rfc1522_binary -- apply RFC 1522 'B' encoding to the given 8bit buffer
  2055.  *
  2056.  * Return: alloc'd buffer containing encoded string
  2057.  */
  2058. char *
  2059. rfc1522_binary (src, srcl)
  2060.     void *src;
  2061.     int   srcl;
  2062. {
  2063.     static char *v =
  2064.             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  2065.     unsigned char *s = (unsigned char *) src;
  2066.     char *ret, *d;
  2067.  
  2068.     d = ret = (char *) fs_get ((size_t) ((((srcl + 2) / 3) * 4) + 1));
  2069.     for (; srcl; s += 3) {    /* process tuplets */
  2070.                 /* byte 1: high 6 bits (1) */
  2071.     *d++ = v[s[0] >> 2];
  2072.                 /* byte 2: low 2 bits (1), high 4 bits (2) */
  2073.     *d++ = v[((s[0] << 4) + (--srcl ? (s[1] >> 4) : 0)) & 0x3f];
  2074.                 /* byte 3: low 4 bits (2), high 2 bits (3) */
  2075.     *d++ = srcl ? v[((s[1] << 2) + (--srcl ? (s[2] >> 6) :0)) & 0x3f] :'=';
  2076.                 /* byte 4: low 6 bits (3) */
  2077.     *d++ = srcl ? v[s[2] & 0x3f] : '=';
  2078.     if(srcl)
  2079.       srcl--;        /* count third character if processed */
  2080.     }
  2081.  
  2082.     *d = '\0';            /* tie off string */
  2083.     return(ret);        /* return the resulting string */
  2084. }
  2085.